package org.budo.icbc.sdk.util;

import java.io.IOException;
import java.math.BigInteger;

import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;

import sun.misc.BASE64Decoder;

/**
 * @author lmw
 */
public class SM2Utils {
    // 生成随机秘钥对
    public static void generateKeyPair() throws IllegalArgumentException {
        SM2 sm2 = SM2.Instance();
        AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair();
        ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate();
        ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
        BigInteger privateKey = ecpriv.getD();
        ECPoint publicKey = ecpub.getQ();

        System.out.println("SM2公钥:" + Util.byteToHex(publicKey.getEncoded()));
        System.out.println("SM2私钥: " + Util.byteToHex(privateKey.toByteArray()));
    }

    // 数据加密
    public static String encrypt(byte[] publicKey, byte[] data) throws IOException {
        if (publicKey == null || publicKey.length == 0) {
            return null;
        }

        if (data == null || data.length == 0) {
            return null;
        }

        byte[] source = new byte[data.length];
        System.arraycopy(data, 0, source, 0, data.length);

        Cipher cipher = new Cipher();
        SM2 sm2 = SM2.Instance();
        ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);

        ECPoint c0 = cipher.Init_enc(sm2, userKey);
        cipher.Encrypt(source);
        byte[] c3 = new byte[32];
        cipher.Dofinal(c3);

        String c0hexStr = Util.getHexString(c0.getEncoded());
        // C1 C2 C3拼装成加密字串
        // 银行加密机截取2位04
        byte[] c1 = Util.hexToByte(c0hexStr.substring(2));
        byte[] data1 = new byte[c1.length + source.length + c3.length];
        System.arraycopy(c1, 0, data1, 0, c1.length);
        System.arraycopy(c3, 0, data1, c1.length, c3.length);
        System.arraycopy(source, 0, data1, c1.length + c3.length, source.length);

        String base64Str = BASE64Encoder.encode(data1);
        return base64Str;
    }

    // 数据解密16进制字符串
    public static byte[] decryptBase64(byte[] privateKey, String encryptedDataBase64) throws IOException {
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] bytes = decoder.decodeBuffer(encryptedDataBase64);
        String encryptedData = Util.byteToHex(bytes);
        // 补全04
        return decryptString(privateKey, "04" + encryptedData);
    }

    // 数据解密16进制字符串
    public static byte[] decryptString(byte[] privateKey, String encryptedData) throws IOException {
        String c1 = encryptedData.substring(0, 130);
        String c3 = encryptedData.substring(130, 130 + 64);
        String c2 = encryptedData.substring(130 + 64);
        encryptedData = c1 + c2 + c3;
        return decrypt(privateKey, Util.hexToByte(encryptedData));
    }

    // 数据解密
    public static byte[] decrypt(byte[] privateKey, byte[] encryptedData) throws IOException {
        if (privateKey == null || privateKey.length == 0) {
            return null;
        }

        if (encryptedData == null || encryptedData.length == 0) {
            return null;
        }
        
        // 加密字节数组转换为十六进制的字符串 长度变为encryptedData.length * 2
        String data = Util.byteToHex(encryptedData);
        /***
         * 分解加密字串 （C1 = C1标志位2位 + C1实体部分128位 = 130） （C3 = C3实体部分64位 = 64） （C2 =
         * encryptedData.length * 2 - C1长度 - C2长度）
         */
        
        byte[] c1Bytes = Util.hexToByte(data.substring(0, 130));
        int c2Len = encryptedData.length - 97;
        byte[] c3 = Util.hexToByte(data.substring(130 + 2 * c2Len, 194 + 2 * c2Len));

        byte[] c2 = Util.hexToByte(data.substring(130, 130 + 2 * c2Len));

        SM2 sm2 = SM2.Instance();
        BigInteger userD = new BigInteger(1, privateKey);

        // 通过C1实体字节来生成ECPoint
        ECPoint c1 = sm2.ecc_curve.decodePoint(c1Bytes);
        Cipher cipher = new Cipher();
        cipher.Init_dec(userD, c1);
        cipher.Decrypt(c2);
        cipher.Dofinal(c3);

        // 返回解密结果
        return c2;
    }
}
